
message(STATUS "")
message(STATUS "#=======================================================#")
message(STATUS "#               START CONFIG LIBFF_UTILS                #")
message(STATUS "#=======================================================#")

#=======================================================#
# 				Create static Library 					#
#=======================================================#
set(TARGET_NAME ff_utils)
add_library(${TARGET_NAME} STATIC)


#=======================================================#
# 					Source File	 						#
#=======================================================#
target_sources(${TARGET_NAME} PRIVATE
	${FFUTILS_INCLUDE_PATH}/ff_utils.h
	ff_utils.cpp
	ff_task.cpp
	#Allocator.cpp
)


#=======================================================#
# 						Defines	 						#
#=======================================================#
target_compile_definitions(${TARGET_NAME} PUBLIC "FEIFEI_UTILS")


#=======================================================#
# 				    Include Path	 					#
#=======================================================#
include_directories("${FFUTILS_INCLUDE_PATH}")


#=======================================================#
# 						CUDA	 						#
#-------------------------------------------------------#
# C:\Program Files\CMake\share\cmake-3.19\Modules\FindCUDA.cmake\FindCUDA.cmake
# https://cmake.org/cmake/help/latest/module/FindCUDAToolkit.html?highlight=cuda
# "FindCUDA()" is deprecated since version 3.10, use "enable_language(CUDA)" instead
#=======================================================#
if(ENABLE_CUDA)
	message(STATUS "")
    message(STATUS "Find CUDA...")
	target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_CUDA")
	
	if(ENABLE_NVCC)
		message(STATUS "Using nvcc compiler.")
		target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_NVCC")
		target_sources(${TARGET_NAME} PRIVATE
			${FFUTILS_INCLUDE_PATH}/ff_utils_cuda.cuh
			ff_utils_cuda.cu
		)
	else()
		message(STATUS "Using cu driver.")	
		target_sources(${TARGET_NAME} PRIVATE
			${FFUTILS_INCLUDE_PATH}/ff_utils_cuda.h
			ff_utils_cuda.cpp
		)
	endif()
		
	set_property(TARGET ${TARGET_NAME} PROPERTY CUDA_ARCHITECTURES OFF)
	#set_target_properties(${TARGET_NAME} PROPERTIES CUDA_ARCHITECTURES ${MY_CUDA_ARCHITECTURES})
	enable_language(CUDA)
	
	if(WIN32)
		find_package(CUDA)

		target_include_directories(${TARGET_NAME} PRIVATE ${CUDA_INCLUDE_DIRS})
		link_directories(${TARGET_NAME} ${CUDAToolkit_LIBRARY_DIR}/lib/x64/)
		target_link_libraries(${TARGET_NAME} ${CUDA_LIBRARIES})
		target_link_libraries(${TARGET_NAME} ${CUDA_CUFFT_LIBRARIES})

		message(STATUS "CUDA_VERSION_STRING = ${CUDA_VERSION_STRING}") 			# CUDA_VERSION/CUDA_VERSION_MAJOR/CUDA_VERSION_MINOR
		message(STATUS "CUDA_HAS_FP16 = ${CUDA_HAS_FP16}")
		message(STATUS "CUDA_SDK_ROOT_DIR = ${CUDA_SDK_ROOT_DIR}")
		message(STATUS "CUDA_TOOLKIT_ROOT_DIR = ${CUDA_TOOLKIT_ROOT_DIR}")		# /NVIDIA GPU Computing Toolkit/CUDA/v11.0
		message(STATUS "CUDA_INCLUDE_DIRS = ${CUDA_INCLUDE_DIRS}") 				# /NVIDIA GPU Computing Toolkit/CUDA/v11.0/include
		
		message(STATUS "CUDA_CUDA_LIBRARY = ${CUDA_CUDA_LIBRARY}") 				# cuda.lib
		message(STATUS "CUDA_cudadevrt_LIBRARY = ${CUDA_cudadevrt_LIBRARY}")	# cudadevrt.lib			
		message(STATUS "CUDA_LIBRARIES = ${CUDA_LIBRARIES}") 					# cudart_static.lib
		message(STATUS "CUDA_CUBLAS_LIBRARIES = ${CUDA_CUBLAS_LIBRARIES}")		# cublas.lib
		message(STATUS "CUDA_CUFFT_LIBRARIES = ${CUDA_CUFFT_LIBRARIES}")		# cufft.lib
		message(STATUS "CUDA_curand_LIBRARY = ${CUDA_curand_LIBRARY}")			# curand.lib
		message(STATUS "CUDA_nppc_LIBRARY = ${CUDA_nppc_LIBRARY}")				# nppc.lib
		message(STATUS "CUDA_npps_LIBRARY = ${CUDA_npps_LIBRARY}")				# npps.lib
	endif()

	if(UNIX)
		set(CMAKE_CUDA_COMPILER "/usr/local/cuda/bin/nvcc" CACHE INTERNAL "catch for upgrade usage")
		find_package(CUDAToolkit)

		target_include_directories(${TARGET_NAME} PRIVATE ${CUDAToolkit_INCLUDE_DIRS})
		link_directories(${TARGET_NAME} ${CUDAToolkit_LIBRARY_DIR})
		target_link_libraries(${TARGET_NAME} CUDA::cudart_static)
		target_link_libraries(${TARGET_NAME} CUDA::cufft_static)

		message(STATUS "CUDAToolkit_FOUND = ${CUDAToolkit_FOUND}")
		message(STATUS "CUDAToolkit_VERSION = ${CUDAToolkit_VERSION}")
		message(STATUS "CUDAToolkit_VERSION_PATCH = ${CUDAToolkit_VERSION_PATCH}")
		message(STATUS "CUDAToolkit_BIN_DIR = ${CUDAToolkit_BIN_DIR}")
		message(STATUS "CUDAToolkit_INCLUDE_DIRS = ${CUDAToolkit_INCLUDE_DIRS}")
		message(STATUS "CUDAToolkit_LIBRARY_DIR = ${CUDAToolkit_LIBRARY_DIR}")
		message(STATUS "CUDAToolkit_LIBRARY_ROOT = ${CUDAToolkit_LIBRARY_ROOT}")
		message(STATUS "CUDAToolkit_TARGET_DIR = ${CUDAToolkit_TARGET_DIR}")
		message(STATUS "CUDAToolkit_NVCC_EXECUTABLE = ${CUDAToolkit_NVCC_EXECUTABLE}")
	endif()
endif()

#-------------------------------------------------------#
# workaround for visual studio issue					#
# refer to https://github.com/actions/virtual-environments/issues/3485
#-------------------------------------------------------#
if(ENABLE_CUDA)
	if(WIN32)
		message(STATUS "set c++17 as workaround for vs & cuda issue.")	
		set(CMAKE_CUDA_STANDARD 17) 
	endif()
endif()


#=======================================================#
# 						OpenCL	 						#
#-------------------------------------------------------#
# Add OpenCL folder path to PATH env                    #
# https://cmake.org/cmake/help/latest/module/FindOpenCL.html?highlight=opencl
#=======================================================#
if(ENABLE_OPENCL)
    message(STATUS "Find OpenCL...")
    find_package(OpenCL)

    if(OpenCL_FOUND)
        message(STATUS "OpenCL_FOUND = " "${OpenCL_FOUND}")
        message(STATUS "OpenCL_VERSION_STRING = " "${OpenCL_VERSION_STRING}")
        message(STATUS "OpenCL_VERSION_MAJOR = " "${OpenCL_VERSION_MAJOR}")
        message(STATUS "OpenCL_VERSION_MINOR = " "${OpenCL_VERSION_MINOR}")
        message(STATUS "OpenCL_INCLUDE_DIR = " "${OpenCL_INCLUDE_DIR}")
        message(STATUS "OpenCL_LIBRARY = " "${OpenCL_LIBRARY}")
        
	    target_compile_definitions(${TARGET_NAME} PUBLIC "CL_TARGET_OPENCL_VERSION=${OpenCL_VERSION_MAJOR}${OpenCL_VERSION_MINOR}0")
	    target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_OCL")
        target_include_directories(${TARGET_NAME} PRIVATE ${OpenCL_INCLUDE_DIR})
        target_link_libraries(${TARGET_NAME} ${OpenCL_LIBRARY})
        target_sources(${TARGET_NAME} 
            PRIVATE
                inc/ff_utils_ocl.h
                src/ff_utils_ocl.cpp
        )
    else()    
        message(FATAL_ERROR "couldn't find opencl runtime.")
    endif()
endif()


#=======================================================#
# 						Window	 						#
#=======================================================#
if(ENABLE_WINDOW)
    target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_WINDOW")
    target_sources(${TARGET_NAME} 
        PRIVATE
            ${FFUTILS_INCLUDE_PATH}/ff_utils_window.h
        PRIVATE
            ff_utils_window.cpp
    )
endif()


#=======================================================#
# 				    	OpenGL		 					#
#=======================================================#
if(ENABLE_OPENGL)
	# https://glad.dav1d.de/
	set(GLAD_DIR ${FFUTILS_PROJECT_PATH}/glad/ CACHE INTERNAL "catch for upgrade usage")
	set(GLFW_DIR ${FFUTILS_PROJECT_PATH}/glfw/ CACHE INTERNAL "catch for upgrade usage")
	set(GLM_DIR ${FFUTILS_PROJECT_PATH}/glm/ CACHE INTERNAL "catch for upgrade usage")
	# https://www.assimp.org/index.php/downloads
	set(ASSIMP_DIR ${FFUTILS_PROJECT_PATH}/assimp/ CACHE INTERNAL "catch for upgrade usage")
	set(STDIMG_DIR ${FFUTILS_PROJECT_PATH}/std_image/ CACHE INTERNAL "catch for upgrade usage")
	find_package(OpenGL REQUIRED)
	if(WIN32)
		find_library(LIB_ASSIMP assimp-vc142-mtd.lib PATHS ${ASSIMP_DIR}/lib/)
	else()
		find_library(LIB_ASSIMP assimp PATHS ${ASSIMP_DIR}/lib/)
	endif()
	if(MSVC_VERSION EQUAL 1929)
		find_library(LIB_GLFW glfw3.lib PATHS ${GLFW_DIR}/lib-vc2019/)	
	else()
		message(STATUS "PLEASE ADD YOUR MSVC VERSION AND GLFW PATH HERE!")
	endif()
	
	target_include_directories(${TARGET_NAME} PRIVATE ${OPENGL_INCLUDE_DIR})
	include_directories("${GLAD_DIR}")
	include_directories("${GLFW_DIR}/include/")
	include_directories("${GLM_DIR}")
	include_directories("${ASSIMP_DIR}/include/")
	include_directories("${STDIMG_DIR}/")
	target_link_libraries(${TARGET_NAME} ${OPENGL_LIBRARIES})
	target_link_libraries(${TARGET_NAME} ${LIB_GLFW})
	target_link_libraries(${TARGET_NAME} ${LIB_ASSIMP})
	target_sources(${TARGET_NAME} PRIVATE ${GLAD_DIR}/glad.cpp)

	message(STATUS "MSVC_VERSION: " ${MSVC_VERSION} )
	message(STATUS "OPENGL_FOUND:= ${OPENGL_FOUND}")
	message(STATUS "OPENGL_INCLUDE_DIR:= ${OPENGL_INCLUDE_DIR}")
	message(STATUS "OPENGL_LIBRARY:= ${OPENGL_LIBRARY}")
	message(STATUS "OPENGL_LIBRARIES:= ${OPENGL_LIBRARIES}")
	message(STATUS "GLAD_DIR = ${GLAD_DIR}")
	message(STATUS "GLFW_DIR = ${GLFW_DIR}")
	message(STATUS "GLM_DIR = ${GLM_DIR}")
	message(STATUS "ASSIMP_DIR = ${ASSIMP_DIR}")
	message(STATUS "LIB_ASSIMP = ${LIB_ASSIMP}")
	message(STATUS "STDIMG_DIR = ${STDIMG_DIR}")

	target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_OGL")
    target_sources(${TARGET_NAME} PRIVATE
        ${FFUTILS_INCLUDE_PATH}/ff_utils_ogl.h
        ff_utils_ogl.cpp
    )
endif()


#=======================================================#
# 						Direct3D 						#
#=======================================================#
if(ENABLE_D3D)
    target_compile_definitions(${TARGET_NAME} PUBLIC "FF_UTILS_EN_D3D")
    target_sources(${TARGET_NAME} 
        PRIVATE
            inc/ff_utils_d3d.h
        PRIVATE
            src/ff_utils_d3d.cpp
    )
endif()


#=======================================================#
# 				Compiler Options 						#
#=======================================================#
if(WIN32)
    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")

        message(STATUS "Configured ${PROJECT_NAME} compiler options for MSVC.")
		if(NOT ENABLE_CUDA)
			# [MSVC] Exception Handling Model
			#    https://msdn.microsoft.com/en-us/library/1deeycx5.aspx
			#
			# [MSVC] Enable Run-Time Type Information
			#    https://msdn.microsoft.com/en-us/library/we6hfdy0.aspx
			#
			# [MSVC] Buffer Security Check
			#    https://msdn.microsoft.com/en-us/library/8dbf701c.aspx
			target_compile_options(${TARGET_NAME} PRIVATE
				/EHsc # Catches only C++ exceptions and assumes
					  # functions declared as extern "C" never throw a C++ exception.
				/GR-  # Disables run-time type information.
				/GS-) # Disables detection of buffer overruns.

			# [MSVC] Warning Level
			#    https://msdn.microsoft.com/en-us/library/thxezb7y.aspx
			target_compile_options(${TARGET_NAME} PRIVATE
				/W3  # Enable warning level 3.
				/WX-) # donot Treat warnings as errors.	
		endif()	
        target_compile_options(${TARGET_NAME} PRIVATE
			/D "_UNICODE") 
			#/D "UNICODE")
    else()
        message(FATAL_ERROR "Using unknown compiler")
    endif()

elseif(UNIX)
    if(METROHASH_ENABLE_WERROR)
        target_compile_options(${TARGET_NAME} PRIVATE -Werror)
    endif()

    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
        message(STATUS "Configured ${PROJECT_NAME} compiler options for GCC.")

		if(NOT ENABLE_NVCC) # for gcc compiler only
        # SEE: https://gcc.gnu.org/onlinedocs/gcc-6.2.0/gcc/Option-Summary.html#Option-Summary
        # for a list of all options and documentation.
        target_compile_options(${TARGET_NAME} PRIVATE -pthread -fPIC)

        # [GCC] Exceptions
        #   https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
        #
        # [GCC] Options Controlling C++ Dialect
        #   https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/C_002b_002b-Dialect-Options.html
        target_compile_options(${TARGET_NAME} PRIVATE
            #-fno-exceptions  # Disable exception handling support.
            -fexceptions  # enable exception handling support.
            -fno-rtti)       # Disable run-time type information support.

        # [GCC] Options to Request or Suppress Warnings
        #   https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Warning-Options.html
        target_compile_options(${TARGET_NAME} PRIVATE
            -Wall
            -Wextra
            -Wpedantic)
		endif()

    elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
        message(WARNING "Clang is untested.")
        target_compile_options(${TARGET_NAME} PRIVATE -pthread -fPIC)

    else()
        message(FATAL_ERROR "Using unknown compiler.")
    endif()
endif()

set_target_properties(${TARGET_NAME} PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED ON
    CXX_EXTENSIONS OFF
    POSITION_INDEPENDENT_CODE ON
)

#=======================================================#
# 					Sub Project 						#
#=======================================================#


#=======================================================#
# 					shell command						#
#=======================================================#
# https://blog.csdn.net/qq_38410730/article/details/102797448
#add_custom_target(CopyTask ALL DEPENDS COPY_LIB_CMD ${TARGET_NAME})
if(WIN32)
	set(out_dir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})
elseif(UNIX)
	set(out_dir ${CMAKE_CURRENT_BINARY_DIR})
endif()

add_custom_command(TARGET ${TARGET_NAME}
	PRE_BUILD
    COMMAND ${CMAKE_COMMAND} -E make_directory ${FFUTILS_LIBRARY_PATH}
)
add_custom_command(TARGET ${TARGET_NAME}
	POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory ${out_dir} ${FFUTILS_LIBRARY_PATH}
    DEPENDS ${TARGET_NAME}
)

message(STATUS "")
message(STATUS "copy from ${out_dir} to ${FFUTILS_LIBRARY_PATH}")

#=======================================================#
# 					nightly test						#
#=======================================================#


message(STATUS "#================ END CONFIG LIBFF_UTILS ===============#")
message(STATUS "")
